#include "ThreadCache.h"
#include "CentralCache.h"

//ThreadCache（线程缓存）从 CentralCache（中心缓存）获取内存对象
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	// 1.慢开始反馈调节算法（核心优化点）
	// 1、最开始不会一次向central cache一次批量要太多，因为要太多了可能用不完
	// 2、如果你不要这个size大小内存需求，那么batchNum就会不断增长，直到上限
	// 3、size越大，一次向central cache要的batchNum就越小
	// 4、size越小，一次向central cache要的batchNum就越大
	size_t batchNum = std::min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));
	//若本次获取数量达到了当前 MaxSize（说明可能不够用），则下次将 MaxSize 加 1（逐步增加批量）
	if(_freeLists[index].MaxSize() == batchNum)
	{
		_freeLists[index].MaxSize() += 1;
	}

	//2. 从中心缓存批量获取内存
	void* start = nullptr;
	void* end = nullptr;
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
	assert(actualNum > 1);//确保至少获取到 2 个内存块

	// 3. 补充线程缓存并返回一个内存块

	//当前线程的内存请求只需要 1 个内存块，多余的内存块存入线程缓存，
	//供该线程后续的同规格内存请求使用，避免频繁向中心缓存申请，提高效率。
	if(actualNum == 1)
	{
		assert(start == end);
		return start;
	}
	else
	{
		//若获取到多个内存块：
		//用 _freeLists[index].PushRange 将 start 之后的所有内存块
		//（从 NextObj(start) 到 end）存入线程缓存的对应链表中。
		//只返回 start 指向的第一个内存块给当前请求者。
		_freeLists[index].PushRange(NextObj(start), end, actualNum-1);
    	return start;
	}
	//这段代码的核心价值在于：

	//1.动态调节批量大小：通过慢开始算法，根据实际内存需求自动调整从中心缓存获取的批量数量，
	//平衡效率和内存利用率。

	//2.减少锁竞争：线程缓存从中心缓存一次获取多个内存块，减少了与全局共享的中心缓存的交互次数，
	//从而减少了锁竞争（中心缓存操作需要加锁）。

	//3.线程私有缓存：获取的多余内存块存入线程私有的缓存，
	//后续同线程的内存请求可以直接从线程缓存快速获取，无需加锁。
}


//从线程缓存申请内存
void* ThreadCache::Allocate(size_t size)
{
	// 1. 检查申请的大小是否在缓存管理范围内（最大256KB）
	assert(size <= MAX_BYTES);
	// 2. 计算对齐后的大小（比如5字节→8字节，按之前的对齐规则）
	size_t alignSize = SizeClass::RoundUp(size);
	// 3. 计算该大小对应的自由链表索引（比如8字节对应索引0）
	size_t index = SizeClass::Index(size);

	// 4. 先查自己的小仓库：如果对应链表有可用内存块，直接取一个
	if (!_freeLists[index].Empty())
	{
		return _freeLists[index].Pop();// 从链表头部取一个内存块
	}
	// 5. 自己的仓库空了，就去“中心仓库”（CentralCache）批量拿一批
	else
	{
		return FetchFromCentralCache(index, alignSize);
	}
}

//ThreadCache内存释放
//将内存块归还给线程缓存，并在适当的时候将多余的内存块归还给中心缓存CentralCache
void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_BYTES);

	// 找对映射的自由链表桶，对象插入进入
	size_t index = SizeClass::Index(size);
	_freeLists[index].Push(ptr);

	// 当链表长度大于一次批量申请的内存时就开始还一段list给central cache
	//避免单个线程缓存囤积过多空闲内存，导致其他线程内存不足，实现内存的均衡利用
	if(_freeLists[index].Size() >= _freeLists[index].MaxSize())
	{
		ListTooLong(_freeLists[index], size);
	}
}

void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	list.PopRange(start, end, list.MaxSize());

	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}